﻿// Type: VSFile.Project.ProjectFile`1
// Assembly: VSFile, Version=1.2.1.0, Culture=neutral, PublicKeyToken=null
// Assembly location: C:\Downloads\VSFile.dll

using System;
using System.Collections.Generic;
using System.Xml;

namespace ReadSolutionContents
{
    /// <summary>
    /// Represents a Visual Studio project file.
    /// 
    /// </summary>
    /// <typeparam name="T">Type of Visual Studio source file referenced in this Visual Studio project file.
    ///             </typeparam>
    public abstract class ProjectFile<T> : VisualStudioFile where T : SourceFile
    {
        /// <summary>
        /// Namespace used in project file.
        /// 
        /// </summary>
        private const string Namespace = "http://schemas.microsoft.com/developer/msbuild/2003";
        /// <summary>
        /// Namespace prefix used in XPath expressions.
        /// 
        /// </summary>
        private const string NamespacePrefix = "msb";
        /// <summary>
        /// Name of project.
        /// 
        /// </summary>
        private string m_projectName;
        /// <summary>
        /// File extension of source files referenced in this project file.
        /// 
        /// </summary>
        private string m_sourceFileExtension;
        /// <summary>
        /// Source files referenced in this project file.
        /// 
        /// </summary>
        private List<T> m_sourceFiles;

        /// <summary>
        /// Get project name.
        /// 
        /// </summary>
        /// 
        /// <value>
        /// String representing project name.
        /// 
        /// </value>
        public string ProjectName
        {
            get
            {
                if (string.IsNullOrEmpty(this.m_projectName))
                    this.m_projectName = this.FileNameNoExtension;
                return this.m_projectName;
            }
        }

        /// <summary>
        /// Get file extension of Visual Studio source files referenced in this project file.
        /// 
        /// </summary>
        /// 
        /// <value>
        /// String representing Visual Studio source file extension.
        /// 
        /// </value>
        public string SourceFileExtension
        {
            get
            {
                return this.m_sourceFileExtension;
            }
        }

        /// <summary>
        /// Get Visual Studio source files referenced in this project file.
        /// 
        /// </summary>
        /// 
        /// <value>
        /// Enumerable collection of objects representing Visual Studio source
        ///             files referenced in this project file.
        /// 
        /// </value>
        public IEnumerable<T> SourceFiles
        {
            get
            {
                return (IEnumerable<T>)this.m_sourceFiles;
            }
        }

        /// <summary>
        /// Constructor for specifying file path.
        /// 
        /// </summary>
        /// <param name="fileExtension">String representing file extension of Visual Studio project file.
        ///             </param><param name="filePath">String representing path to Visual Studio project file.
        ///             </param><param name="sourceFileExtension">String representing file extension of Visual Studio source files
        ///             referenced in this Visual Studio project file.
        ///             </param>
        protected ProjectFile(string fileExtension, string filePath, string sourceFileExtension)
            : this((string)null, fileExtension, filePath, sourceFileExtension)
        {
        }

        /// <summary>
        /// Constructor for specifying project name.
        /// 
        /// </summary>
        /// <param name="projectName">String representing Visual Studio project name.
        ///             </param><param name="fileExtension">String representing file extension of Visual Studio project file.
        ///             </param><param name="filePath">String representing path to Visual Studio project file.
        ///             </param><param name="sourceFileExtension">String representing file extension of Visual Studio source files
        ///             referenced in this Visual Studio project file.
        ///             </param>
        protected ProjectFile(string projectName, string fileExtension, string filePath, string sourceFileExtension)
            : base(fileExtension, filePath)
        {
            if (string.IsNullOrEmpty(sourceFileExtension))
                throw new ArgumentException();
            this.m_projectName = projectName;
            this.m_sourceFileExtension = sourceFileExtension;
            this.m_sourceFiles = new List<T>();
        }

        /// <summary>
        /// Create instance of source file with given file path.
        /// 
        /// </summary>
        /// <param name="filePath">String representing file path.
        ///             </param>
        /// <returns>
        /// Instance of source file with given file path.
        /// 
        /// </returns>
        protected abstract T CreateSourceFile(string filePath);

        /// <summary>
        /// Read file.
        /// 
        /// </summary>
        protected override void ReadFile()
        {
            this.ClearFiles();
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(this.FilePath);
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
            namespaceManager.AddNamespace("msb", "http://schemas.microsoft.com/developer/msbuild/2003");
            foreach (XmlNode node in xmlDocument.SelectNodes("/msb:Project/msb:ItemGroup/msb:Compile", namespaceManager))
                this.AddSourceFile(node, namespaceManager);
        }

        /// <summary>
        /// Add source file contained within given XML node and namespace manager.
        /// 
        /// </summary>
        /// <param name="node">XmlNode containing source file.
        ///             </param><param name="namespaceManager">XmlNamespaceManager managing XML namespace used in project file.
        ///             </param>
        private void AddSourceFile(XmlNode node, XmlNamespaceManager namespaceManager)
        {
            XmlNode xmlNode1 = node.SelectSingleNode("msb:AutoGen", namespaceManager);
            if (xmlNode1 != null)
            {
                string innerText = xmlNode1.InnerText;
                if (innerText != null && innerText.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase))
                    return;
            }
            XmlNode xmlNode2 = node.SelectSingleNode("@Include", namespaceManager);
            if (xmlNode2 == null)
                return;
            string relativePath = xmlNode2.Value;
            if (relativePath == null || !relativePath.EndsWith(this.SourceFileExtension, StringComparison.OrdinalIgnoreCase))
                return;
            this.m_sourceFiles.Add(this.CreateSourceFile(this.GetFullPath(relativePath)));
        }

        /// <summary>
        /// Clear referenced source files.
        /// 
        /// </summary>
        private void ClearFiles()
        {
            this.m_sourceFiles.Clear();
        }

        /// <summary>
        /// XPath expressions used to select elements and attributes in project file.
        /// 
        /// </summary>
        private static class XPath
        {
            public const string AutoGenElement = "msb:AutoGen";
            public const string CompileElement = "/msb:Project/msb:ItemGroup/msb:Compile";
            public const string IncludeAttribute = "@Include";
        }
    }
}
